﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TheVoiceOfSnake.Enums;

namespace TheVoiceOfSnake.Models
{
    public class Snake
    {
        public SnakePart Head { get; set; }
        public List<SnakePart> Body { get; set; }
        public SnakePart Tail { get; set; }

        public Snake() 
        {
            Head = new SnakePart();
            Body = new List<SnakePart>();
            Tail = new SnakePart();
        }

        public Snake(int hx, int hy, FieldState hState, int tx, int ty, FieldState tState)
        {
            Head = new SnakePart(hx, hy, hState);
            Body = new List<SnakePart>();
            Tail = new SnakePart(tx, ty, tState);
        }

        public void MoveHead(int numOfRows, int numOfColumns)
        {
            switch (Head.State)
            {
                case FieldState.SnakeHeadUp:
                    Head.X = Head.X == 0 ? numOfRows - 1 : Head.X - 1;
                    break;

                case FieldState.SnakeHeadRight:
                    Head.Y = Head.Y == numOfColumns - 1 ? 0 : Head.Y + 1;
                    break;

                case FieldState.SnakeHeadDown:
                    Head.X = Head.X == numOfRows - 1 ? 0 : Head.X + 1;
                    break;

                default:    // case FieldState.SnakeHeadLeft
                    Head.Y = Head.Y == 0 ? numOfColumns - 1 : Head.Y - 1;
                    break;
            }
        }

        public void MoveTail(int numOfRows, int numOfColumns) 
        {
            if (Body.Count > 0)
            {
                switch (Body.Last().State)
                {
                    case FieldState.SnakeBodyVertical:
                        if (Tail.State == FieldState.SnakeTailUp)
                        {
                            Tail.X = Tail.X == numOfRows - 1 ? 0 : Tail.X + 1;
                        }
                        else
                        {
                            Tail.X = Tail.X == 0 ? numOfRows - 1 : Tail.X - 1;
                        }
                        break;

                    case FieldState.SnakeBodyHorizontal:
                        if (Tail.State == FieldState.SnakeTailRight)
                        {
                            Tail.Y = Tail.Y == 0 ? numOfColumns - 1 : Tail.Y - 1;
                        }
                        else
                        {
                            Tail.Y = Tail.Y == numOfColumns - 1 ? 0 : Tail.Y + 1;
                        }
                        break;

                    case FieldState.SnakeBodyLeftUp:
                        if (Tail.State == FieldState.SnakeTailUp)
                        {
                            Tail.X = Tail.X == numOfRows - 1 ? 0 : Tail.X + 1;
                            Tail.State = FieldState.SnakeTailRight;
                        }
                        else
                        {
                            Tail.Y = Tail.Y == numOfColumns - 1 ? 0 : Tail.Y + 1;
                            Tail.State = FieldState.SnakeTailDown;
                        }
                        break;

                    case FieldState.SnakeBodyRightUp:
                        if (Tail.State == FieldState.SnakeTailUp)
                        {
                            Tail.X = Tail.X == numOfRows - 1 ? 0 : Tail.X + 1;
                            Tail.State = FieldState.SnakeTailLeft;
                        }
                        else
                        {
                            Tail.Y = Tail.Y == 0 ? numOfColumns - 1 : Tail.Y - 1;
                            Tail.State = FieldState.SnakeTailDown;
                        }
                        break;

                    case FieldState.SnakeBodyLeftDown:
                        if (Tail.State == FieldState.SnakeTailDown)
                        {
                            Tail.X = Tail.X == 0 ? numOfRows - 1 : Tail.X - 1;
                            Tail.State = FieldState.SnakeTailRight;
                        }
                        else
                        {
                            Tail.Y = Tail.Y == numOfColumns - 1 ? 0 : Tail.Y + 1;
                            Tail.State = FieldState.SnakeTailUp;
                        }
                        break;

                    default:    // case FieldState.SnakeBodyRightDown
                        if (Tail.State == FieldState.SnakeTailDown)
                        {
                            Tail.X = Tail.X == 0 ? numOfRows - 1 : Tail.X - 1;
                            Tail.State = FieldState.SnakeTailLeft;
                        }
                        else
                        {
                            Tail.Y = Tail.Y == 0 ? numOfColumns - 1 : Tail.Y - 1;
                            Tail.State = FieldState.SnakeTailUp;
                        }
                        break;
                }
            }
            else 
            {
                switch (Head.State)
                {
                    case FieldState.SnakeHeadUp:
                        Tail.X = Tail.X == 0 ? numOfRows - 1 : Tail.X - 1;
                        Tail.State = FieldState.SnakeTailDown;
                        break;

                    case FieldState.SnakeHeadRight:
                        Tail.Y = Tail.Y == numOfColumns - 1 ? 0 : Tail.Y + 1;
                        Tail.State = FieldState.SnakeTailLeft;
                        break;

                    case FieldState.SnakeHeadDown:
                        Tail.X = Tail.X == numOfRows - 1 ? 0 : Tail.X + 1;
                        Tail.State  = FieldState.SnakeTailUp;
                        break;

                    default:    // case FieldState.SnakeHeadLeft
                        Tail.Y = Tail.Y == 0 ? numOfColumns - 1 : Tail.Y - 1;
                        Tail.State = FieldState.SnakeTailRight;
                        break;
                }
            }
        }

        public void MoveBody(IEnumerable<SnakePart> bodyList, int numOfRows, int numOfColumns)
        {
            foreach (var body in bodyList)
            {
                switch (Head.State)
                {
                    case FieldState.SnakeHeadUp:
                        body.X = body.X == 0 ? numOfRows - 1 : body.X - 1;
                        body.State = FieldState.SnakeBodyVertical;
                        break;

                    case FieldState.SnakeHeadRight:
                        body.Y = body.Y == numOfColumns - 1 ? 0 : body.Y + 1;
                        body.State = FieldState.SnakeBodyHorizontal;
                        break;

                    case FieldState.SnakeHeadDown:
                        body.X = body.X == numOfRows - 1 ? 0 : body.X + 1;
                        body.State = FieldState.SnakeBodyVertical;
                        break;

                    default:    // case FieldState.SnakeHeadLeft
                        body.Y = body.Y == 0 ? numOfColumns - 1 : body.Y - 1;
                        body.State = FieldState.SnakeBodyHorizontal;
                        break;
                }
            }
        }

        public void Turn(int numOfRows, int numOfColumns, Direction direction, GameField[,] gameBoard) 
        {
            if (direction == Direction.Right && Head.State != FieldState.SnakeHeadRight && Head.State != FieldState.SnakeHeadLeft)
            {
                gameBoard[Head.X, Head.Y].FieldState = FieldState.Empty;
                MoveBodyAndTail(numOfRows, numOfColumns, gameBoard);
                if (Body.Count > 0) 
                {
                    if (Head.State == FieldState.SnakeHeadUp)
                    {
                        Body.First().State = FieldState.SnakeBodyRightDown;
                    }
                    else
                    {
                        Body.First().State = FieldState.SnakeBodyRightUp;
                    }
                }
                Head.Y = Head.Y == numOfColumns - 1 ? 0 : Head.Y + 1;
                Head.State = FieldState.SnakeHeadRight;
            }
            else if (direction == Direction.Left && Head.State != FieldState.SnakeHeadRight && Head.State != FieldState.SnakeHeadLeft)
            {
                gameBoard[Head.X, Head.Y].FieldState = FieldState.Empty;
                MoveBodyAndTail(numOfRows, numOfColumns, gameBoard);
                if (Body.Count > 0)
                {
                    if (Head.State == FieldState.SnakeHeadUp)
                    {
                        Body.First().State = FieldState.SnakeBodyLeftDown;
                    }
                    else
                    {
                        Body.First().State = FieldState.SnakeBodyLeftUp;
                    }
                }
                else 
                {
                    Tail.State = FieldState.SnakeTailRight;
                }
                Head.Y = Head.Y == 0 ? numOfColumns - 1 : Head.Y - 1;
                Head.State = FieldState.SnakeHeadLeft;
            }
            else if (direction == Direction.Up && Head.State != FieldState.SnakeHeadUp && Head.State != FieldState.SnakeHeadDown)
            {
                gameBoard[Head.X, Head.Y].FieldState = FieldState.Empty;
                MoveBodyAndTail(numOfRows, numOfColumns, gameBoard);
                if (Body.Count > 0)
                {
                    if (Head.State == FieldState.SnakeHeadLeft)
                    {
                        Body.First().State = FieldState.SnakeBodyRightUp;
                    }
                    else
                    {
                        Body.First().State = FieldState.SnakeBodyLeftUp;
                    }
                }
                else
                {
                    Tail.State = FieldState.SnakeTailDown;
                }
                Head.X = Head.X == 0 ? numOfRows - 1 : Head.X - 1;
                Head.State = FieldState.SnakeHeadUp;
            }
            else if (direction == Direction.Down && Head.State != FieldState.SnakeHeadUp && Head.State != FieldState.SnakeHeadDown)
            {
                gameBoard[Head.X, Head.Y].FieldState = FieldState.Empty;
                MoveBodyAndTail(numOfRows, numOfColumns, gameBoard);
                if (Body.Count > 0)
                {
                    if (Head.State == FieldState.SnakeHeadLeft)
                    {
                        Body.First().State = FieldState.SnakeBodyRightDown;
                    }
                    else
                    {
                        Body.First().State = FieldState.SnakeBodyLeftDown;
                    }
                }
                else
                {
                    Tail.State = FieldState.SnakeTailUp;
                }
                Head.X = Head.X == numOfRows - 1 ? 0 : Head.X + 1;
                Head.State = FieldState.SnakeHeadDown;
            }
        }

        public void MoveBodyAndTail(int numOfRows, int numOfColumns, GameField[,] gameBoard)
        {
            List<SnakePart> temp = new List<SnakePart>();
            if (Body.Count > 0)
            {
                temp.Add(new SnakePart(Body.First().X, Body.First().Y, Body.First().State));
                MoveBody(temp, numOfRows, numOfColumns);
                SnakePart[] array = new SnakePart[Body.Count - 1];
                Body.CopyTo(0, array, 0, Body.Count - 1);
                temp.AddRange(array);
                gameBoard[Tail.X, Tail.Y].FieldState = FieldState.Empty;
                MoveTail(numOfRows, numOfColumns);
                Body.Clear();
                Body.AddRange(temp);
            }
            else 
            {
                gameBoard[Tail.X, Tail.Y].FieldState = FieldState.Empty;
                MoveTail(numOfRows, numOfColumns);
            }
        }

        public void AddPoint(int numOfRows, int numOfColumns)
        {
            SnakePart newBodyPart = new SnakePart(Head.X, Head.Y, FieldState.SnakeBodyHorizontal);
            switch (Head.State)
            {
                case FieldState.SnakeHeadUp:
                    Head.X = Head.X == 0 ? numOfRows - 1 : Head.X - 1;
                    newBodyPart.State = FieldState.SnakeBodyVertical;
                    break;

                case FieldState.SnakeHeadRight:
                    Head.Y = Head.Y == numOfColumns - 1 ? 0 : Head.Y + 1;
                    newBodyPart.State = FieldState.SnakeBodyHorizontal;
                    break;

                case FieldState.SnakeHeadDown:
                    Head.X = Head.X == numOfRows - 1 ? 0 : Head.X + 1;
                    newBodyPart.State = FieldState.SnakeBodyVertical;
                    break;

                default:    // case FieldState.SnakeHeadLeft
                    Head.Y = Head.Y == 0 ? numOfColumns - 1 : Head.Y - 1;
                    newBodyPart.State = FieldState.SnakeBodyHorizontal;
                    break;
            }

            List<SnakePart> temp = new List<SnakePart>() { newBodyPart };
            temp.AddRange(Body);
            Body.Clear();
            Body.AddRange(temp);
        }
    }
}
